# -*- coding: utf-8 -*-
# @Time    : 2024/6/11 15:32
# @Author  : yujiahao
# @File    : 13_read_write_file.py
# @description:Python中的读写文件

r'''


有如下文件，但是没有相应的软件打开，想不想看😏？

【美女模特空姐护士联系方式.txt】

让你开发一个软件，可以打开此文件，你需要什么参数？
    文件路径: D:\美女模特空姐护士联系方式.txt
    编码：utf-8，gbk，gb2312....
    模式：只读，只写，追加，写读，读写....
使用Python来读写文件是非常简单的操作,我们使用open()来打开一个文件,获取到文件句柄,然后通过文件句柄就可以进行各种各样的操作了

根据打开方式的不同能够执行的操作会有相应的差异.

    打开文件的方式:

    　　r,w,a
    　　r+,w+,a+
    　　rb,wb,ab
    　　r+b,w+b,a+b

文件读写操作及其应用场景（Java、Scala、Python）：

1. Java 文件读写操作：
   - 底层原理：
     Java 的文件 I/O 操作基于 `java.io` 包，该包提供了大量类和接口来处理不同类型的 I/O 操作。主要的类包括 `FileReader`、`FileWriter`、`FileInputStream`、`FileOutputStream` 和 `RandomAccessFile`。这些类通过调用本地操作系统的文件系统 API 来实现文件的读取和写入操作。Java 的文件 I/O 操作是同步阻塞的，这意味着 I/O 操作会阻塞当前线程，直到操作完成。此外，Java 提供了 NIO（New I/O）库，通过 `java.nio.file` 包提供更高效的非阻塞 I/O 操作。

   - 打开文件：
     - 读取模式：new FileReader("filename")
       注意事项：适用于文本文件，默认使用平台的字符编码。
     - 写入模式：new FileWriter("filename")
       注意事项：创建新文件或覆盖现有文件，默认使用平台的字符编码。
     - 追加模式：new FileWriter("filename", true)
       注意事项：在文件末尾追加内容，不覆盖现有内容。
     - 二进制模式：new FileInputStream("filename"), new FileOutputStream("filename")
       注意事项：适用于二进制文件，如图片、音频等。
     - 读写模式：new RandomAccessFile("filename", "rw")
       注意事项：支持随机访问文件内容，适用于需要读写操作的场景。

   - 读取文件：
     - read()：
       特性：读取单个字符。
       使用方法：int char = reader.read()
       注意事项：适用于逐字符读取，效率较低。
     - readLine()：
       特性：读取一行文本。
       使用方法：String line = reader.readLine()
       注意事项：适用于逐行读取文本文件，需捕获IOException异常。
     - readAllBytes()：
       特性：读取所有字节。
       使用方法：byte[] data = fis.readAllBytes()
       注意事项：适用于小型二进制文件，大文件可能导致内存不足。

   - 写入文件：
     - write()：
       特性：将字符串或字节数组写入文件。
       使用方法：writer.write("some text"), fos.write(byteArray)
       注意事项：需要显式调用flush()或close()以确保数据写入文件，捕获IOException异常。

   - 关闭文件：
     - close()：
       特性：关闭文件，释放资源。
       使用方法：reader.close(), writer.close()
       注意事项：应在完成文件操作后关闭文件，避免资源泄漏。

   - 示例代码：
     // 读取文件示例
     try (BufferedReader reader = new BufferedReader(new FileReader("example.txt"))) {
         String line;
         while ((line = reader.readLine()) != null) {
             System.out.println(line);
         }
     } catch (IOException e) {
         e.printStackTrace();
     }

     // 写入文件示例
     try (BufferedWriter writer = new BufferedWriter(new FileWriter("example.txt"))) {
         writer.write("Hello, World!");
     } catch (IOException e) {
         e.printStackTrace();
     }

     // 追加文件示例
     try (BufferedWriter writer = new BufferedWriter(new FileWriter("example.txt", true))) {
         writer.write("\nAppended text.");
     } catch (IOException e) {
         e.printStackTrace();
     }

     // 二进制读取文件示例
     try (FileInputStream fis = new FileInputStream("example.bin")) {
         byte[] data = fis.readAllBytes();
         // 处理二进制数据
     } catch (IOException e) {
         e.printStackTrace();
     }

     // 二进制写入文件示例
     try (FileOutputStream fos = new FileOutputStream("example.bin")) {
         fos.write(new byte[]{0, 1, 2, 3});
     } catch (IOException e) {
         e.printStackTrace();
     }

2. Scala 文件读写操作：
   - 底层原理：
     Scala 的文件 I/O 操作通常依赖于 Java 的 I/O 类，因为 Scala 运行在 JVM 上。Scala 提供了 `scala.io.Source` 类来简化文本文件的读取操作，以及 `java.io.PrintWriter` 类来处理文件写入。对于二进制文件操作，Scala 直接使用 Java 的 `FileInputStream` 和 `FileOutputStream` 类。Scala 还支持通过 `java.nio` 包进行更高效的非阻塞 I/O 操作。

   - 打开文件：
     - 读取模式：Source.fromFile("filename")
       注意事项：适用于文本文件，使用默认字符编码。
     - 写入模式：new PrintWriter(new File("filename"))
       注意事项：创建新文件或覆盖现有文件。
     - 追加模式：new PrintWriter(new FileWriter("filename", true))
       注意事项：在文件末尾追加内容，不覆盖现有内容。
     - 二进制模式：new FileInputStream("filename"), new FileOutputStream("filename")
       注意事项：适用于二进制文件，如图片、音频等。

   - 读取文件：
     - getLines()：
       特性：逐行读取文件内容。
       使用方法：val lines = source.getLines()
       注意事项：适用于逐行处理文本文件，需显式关闭源。
     - read()：
       特性：读取所有字节。
       使用方法：val data = fis.read(buffer)
       注意事项：适用于小型二进制文件，大文件可能导致内存不足。

   - 写入文件：
     - write()：
       特性：将字符串或字节数组写入文件。
       使用方法：writer.write("some text"), fos.write(byteArray)
       注意事项：需要显式调用flush()或close()以确保数据写入文件。

   - 关闭文件：
     - close()：
       特性：关闭文件，释放资源。
       使用方法：source.close(), writer.close()
       注意事项：应在完成文件操作后关闭文件，避免资源泄漏。

   - 示例代码：
     // 读取文件示例
     val source = Source.fromFile("example.txt")
     try {
       for (line <- source.getLines()) {
         println(line)
       }
     } finally {
       source.close()
     }

     // 写入文件示例
     val writer = new PrintWriter(new File("example.txt"))
     try {
       writer.write("Hello, World!")
     } finally {
       writer.close()
     }

     // 追加文件示例
     val appendWriter = new PrintWriter(new FileWriter("example.txt", true))
     try {
       appendWriter.write("\nAppended text.")
     } finally {
       appendWriter.close()
     }

     // 二进制读取文件示例
     val fis = new FileInputStream("example.bin")
     try {
       val data = new Array[Byte](fis.available())
       fis.read(data)
       // 处理二进制数据
     } finally {
       fis.close()
     }

     // 二进制写入文件示例
     val fos = new FileOutputStream("example.bin")
     try {
       fos.write(Array[Byte](0, 1, 2, 3))
     } finally {
       fos.close()
     }

3. Python 文件读写操作：
   - 底层原理：
     Python 的文件 I/O 操作基于 C 语言的标准 I/O 库。Python 提供了内置的 `open()` 函数来打开文件，并返回一个文件对象。文件对象的方法如 `read()`、`write()`、`readline()` 和 `readlines()` 等，都是对底层 C 库函数的封装。Python 的文件操作是同步阻塞的，这意味着 I/O 操作会阻塞当前线程，直到操作完成。Python 还提供了 `io` 模块，支持更高级的 I/O 操作，包括缓冲 I/O 和非缓冲 I/O。

   - 打开文件：
     - 读取模式：file = open('filename', 'r')
       注意事项：适用于文本文件，默认使用平台的字符编码。
     - 写入模式：file = open('filename', 'w')
       注意事项：创建新文件或覆盖现有文件，默认使用平台的字符编码。
     - 追加模式：file = open('filename', 'a')
       注意事项：在文件末尾追加内容，不覆盖现有内容。
     - 二进制模式：file = open('filename', 'rb'), file = open('filename', 'wb')
       注意事项：适用于二进制文件，如图片、音频等。
     - 读写模式：file = open('filename', 'r+'), file = open('filename', 'w+')
       注意事项：支持同时读写文件内容。

   - 读取文件：
     - read()：
       特性：读取整个文件内容。
       使用方法：content = file.read()
       注意事项：适用于小文件，大文件可能导致内存不足。
     - readline()：
       特性：读取文件的一行。
       使用方法：line = file.readline()
       注意事项：适用于逐行读取文件内容。
     - readlines()：
       特性：读取文件的所有行，返回一个列表。
       使用方法：lines = file.readlines()
       注意事项：适用于小文件，大文件可能导致内存不足。
     - 迭代文件对象：
       特性：逐行读取文件内容。
       使用方法：
         for line in file:
             # 处理每一行
       注意事项：适用于逐行处理大文件。

   - 写入文件：
     - write()：
       特性：将字符串写入文件。
       使用方法：file.write('some text')
       注意事项：需要显式调用flush()或close()以确保数据写入文件。
     - writelines()：
       特性：将字符串列表写入文件。
       使用方法：file.writelines(['line1\n', 'line2\n'])
       注意事项：需要显式调用flush()或close()以确保数据写入文件。

   - 关闭文件：
     - close()：
       特性：关闭文件，释放资源。
       使用方法：file.close()
       注意事项：应在完成文件操作后关闭文件，避免资源泄漏。

   - 上下文管理器：
     - with 语句：
       特性：自动管理文件的打开和关闭。
       使用方法：
         with open('filename', 'r') as file:
             content = file.read()
       注意事项：推荐使用上下文管理器以确保文件正确关闭。

   - 示例代码：
     # 读取文件示例
     with open('example.txt', 'r') as file:
         content = file.read()
         print(content)

     # 写入文件示例
     with open('example.txt', 'w') as file:
         file.write('Hello, World!')

     # 追加文件示例
     with open('example.txt', 'a') as file:
         file.write('\nAppended text.')

     # 逐行读取文件示例
     with open('example.txt', 'r') as file:
         for line in file:
             print(line.strip())

     # 二进制读取文件示例
     with open('example.bin', 'rb') as file:
         binary_content = file.read()

     # 二进制写入文件示例
     with open('example.bin', 'wb') as file:
         file.write(b'\x00\x01\x02\x03')

     # 读写模式示例
     with open('example.txt', 'r+') as file:
         content = file.read()
         file.write('\nNew text.')

【推荐使用with：】

    使用 with 语句的好处
        - 自动关闭文件：with 语句在块结束时自动关闭文件，无需显式调用 close() 方法。
        - 异常处理：即使在块内发生异常，文件也会被正确关闭，避免资源泄露。
        - 代码简洁：减少了显式打开和关闭文件的代码，使代码更简洁易读。

        file 可写成任意变量等，它被称作：文件句柄，文件操作符，或者文件操作对象等。
        open 是Python调用的操作系统（windows,linux,等）的功能，而windows的默认编码方式为gbk，linux默认编码方式为utf-8，所以你的文件用什么编码保存的，就用什么方法打开，一般都是用utf-8。
        mode为打开方式：常见的有r,w,a,r+,w+,a+.rb,wb,ab,等，默认不写是r。
        流程就是打开文件，产生一个文件句柄，对文件句柄进行相应操作，关闭文件。

    示例对比
        不使用 with 语句
            try:
                file = open('example.txt', 'r')
                content = file.read()
                print(content)
            finally:
                file.close()  # 确保文件被关闭
        使用 with 语句
            with open('example.txt', 'r') as file:
                content = file.read()
                print(content)

参数方法：def open(file, mode='r', buffering=None, encoding=None, errors=None, newline=None, closefd=True)

【参数描述】
    1. file
        描述：要打开的文件的名称或路径。
        类型：字符串。
        示例：'example.txt' 或 '/path/to/example.txt'。
    2. mode
        描述：文件打开模式，指定文件的操作类型（读、写、追加等）。
        类型：字符串。
        默认值：'r'（只读模式）。
        常见模式：
        'r'：只读模式（默认）。
        'w'：写入模式（会覆盖文件）。
        'a'：追加模式（在文件末尾添加内容）。
        'b'：二进制模式（如 'rb'、'wb'）。
        '+'：读写模式（如 'r+'、'w+'、'a+'）。
    3. buffering
        描述：设置文件的缓冲策略。
        类型：整数。
        默认值：None（系统默认缓冲策略）。
        选项：
        0：无缓冲（仅适用于二进制模式）。
        1：行缓冲（仅适用于文本模式）。
        大于 1 的整数：固定大小的缓冲区。
        -1：使用系统默认的缓冲策略。
    4. encoding
        描述：设置文件的编码类型（仅适用于文本模式）。
        类型：字符串。
        默认值：None（使用平台默认编码）。
        示例：'utf-8'、'ascii'、'latin-1'。
    5. errors
        描述：设置编码错误处理策略（仅适用于文本模式）。
        类型：字符串。
        默认值：None（默认处理）。
        选项：
        'strict'：默认值，遇到编码错误时引发 ValueError。
        'ignore'：忽略编码错误。
        'replace'：用替代字符（通常是 ?）替换编码错误。
        'backslashreplace'：使用反斜杠转义序列替换编码错误。
        'namereplace'：使用\N{...}替换编码错误。
    6. newline
        描述：控制新行字符的处理方式（仅适用于文本模式）。
        类型：字符串或 None。
        默认值：None（使用默认换行处理）。
        选项：
        ''：不转换换行符。
        '\n'：仅识别换行符 \n。
        '\r'：仅识别换行符 \r'。
        '\r\n'：仅识别换行符 \r\n'。
    7. closefd
        描述：如果文件描述符是由文件名创建的，设置为 True，否则设置为 False。
        类型：布尔值。
        默认值：True。
        注意：如果 file 是文件描述符而不是文件名，则 closefd 必须为 False。

【路径】

    绝对路径：从根目录下开始一直到文件名。
    相对路径：同一个文件夹下面的文件，直接写文件名就可以。

    我们在使用绝对路径的时候因为有\这样程序是不能识别的,所以要用转义或者使用原始字符串这个前面说字符串的时候讲过，这里不再说。

        open('C:\Users\Meet')  #这样程序是不识别的

        解决方法一:
        open('C:\\Users\\Meet') #这样就成功的将 \进行转义   两个\\代表一个\

        解决方法二:
        open(r'C:\Users\Meet') #这样相比上边的还要省事,在字符串的前面加个小r也是转义的意思  推荐使用这种,比如当前注释，不加r会报错

    路径方面我更推荐大家使用相对路劲,因为我把这个程序的整个文件发给你的时候,就可以运行,如果使用绝对路径还需要额外的拷贝外部文件给你
'''




# todo 1、只读




# 设置相对路径
input_path = '../../../input/美女模特空姐护士联系方式.txt'
output_path = '../../../output/写的文件.txt'

# 1.1 r 只读模式
try:
    with open(input_path, mode='r', encoding='utf-8') as f:
        for line in f:
            print(f'只读模式：{line}')
except FileNotFoundError:
    print(f"文件 {input_path} 未找到")
except UnicodeDecodeError:
    print(f"文件 {input_path} 解码错误")

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

# 1.2 rb 只读字节模式

'''
rb的作用:在读取非文本文件的时候,比如要读取mp3,图像,视频等信息的时候就需要用到rb,因为这种数据是没办法直接显示出来的
这个字节的模式是用于传输和存储
'''
try:
    with open(input_path, mode='rb') as f:
        print(f'只读字节模式：{f.read()}')
except FileNotFoundError:
    print(f"文件 {input_path} 未找到")

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

# todo 2.1 覆盖写
'''使用w模式的时候,在打开文件的时候就就会把文件中的所有内容都清空,然后在操作
   wb模式下,不可以指定打开文件的编辑,但是写文件的时候必须将字符串转换成utf-8的bytes数据
'''



with open(output_path, mode='w', encoding='utf-8') as f:
    f.write('Hello, World!')

# 2.2 追加
'''
 - 只要是a或者ab,a+都是在文件的末尾写入,不论光标在任何位置.
 - 在追加模式下,我们写入的内容后追加在文件的末尾
 - a模式如果文件不存在就会创建一个新文件
'''

f1 = open(output_path, mode='a', encoding='utf-8')
msg = f1.write('\n你好哇🤩')
# ab这个模式,自己试一下 没有什么太大的差别

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')


# todo 3、读写模式


'''对于读写模式,必须是先读后写,因为光标默认在开头位置,当读完了以后再进行写入.我们以后使用频率最高的模式就是r+
在r+模式下. 如果读取了内容. 不论读取内容多少. 光标显示的是多少. 再写入 或者操作文件的时候都是在结尾进行的操作
r+模式一定要记住是先读后写 '''

# 这里演示不使用with写法,注意要关闭文件和刷新文件

# 1.1 r+模式

# 看下正确的操作:会追加到末尾

f1 = open(output_path, mode='r+', encoding='utf-8')
msg = f1.read()
f1.write('\n这支烟灭了以后')
f1.flush()
f1.close()
print(msg)


# 看下错误的操作:会覆盖到开头。这样写会把小鬼写在开头,并且读出来的是小鬼之后的内容

# f1 = open(out_path,mode='r+',encoding='utf-8')
# f1.write('小鬼')
# msg = f1.read()
# f1.flush()
# f1.close()
# print(msg)

# todo 3、写读模式
''' 先将所有的内容清空,然后写入.最后读取.但是读取的内容是空的,不常用 '''

f1 = open(output_path, mode='w+', encoding='utf-8')
f1.write('小鬼')
msg = f1.read()
f1.flush()
f1.close()
print(msg)

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')


# todo 4、 其他相关操作


'''
seek(n)光标移动到n位置,注意: 移动单位是byte,所有如果是utf-8的中文部分要是3的倍数

通常我们使用seek都是移动到开头或者结尾

移动到开头:seek(0,0)

移动到当前位置:seek(0,1)

移动到末尾:seek(0,2)

移动1个字:seek(3) 移动光标是按照字节进行移动

使用tell()可以帮我们获取当前光标在什么位置
'''

# 以读写模式打开文件
f = open(input_path, mode="r+", encoding="utf-8")

# 将光标移动到文件开头
f.seek(0)

# 读取文件内容，此时光标移动到文件结尾
content = f.read()
print("Initial content:")
print(content)

# 再次将光标移动到文件开头
f.seek(0)

# 将光标移动到文件结尾
f.seek(0, 2)

# 尝试读取内容，此时应没有内容可读
content2 = f.read()
print("Content after seeking to the end:")
print(content2)

# 将光标移动到文件开头
f.seek(0)
print(content2)
# 写入新内容
f.write("张国荣")
f.flush()

# 打印当前文件指针位置
print("Current file pointer position after writing:", f.tell())

# 关闭文件
f.close()



# todo 5、 修改文件
'''文件修改: 只能将文件中的内容读取到内存中, 将信息修改完毕, 然后将源文件删除, 将新文件的名字改成老文件的名字.'''
import os


# 打开源文件和目标文件
with open(input_path, mode="r", encoding="utf-8") as f1, \
     open(output_path, mode="w", encoding="utf-8") as f2:
    # 逐行读取源文件内容并进行替换
    for line in f1:
        new_line = line.replace("张国荣", "王祖贤")
        f2.write(new_line)

# 删除源文件
os.remove(input_path)

# 重命名新文件为源文件名
os.rename(output_path, input_path)
